//
//  WLProxySession.m
//  WLProxy
//
//  Created by wangpeng on 2022/11/2.
//

#import "WHWLProxySession.h"
#import "wlproxy_invoker.h"
#import "wlproxy_request.h"
#import "wlproxy_define.h"
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <unistd.h>
#import <netinet/tcp.h>
#import "wherr.h"
#import <XmlHelper/XmlHelper.h>
#import "WHProxyManage.h"
#import "WHProxyInvoker.h"

#define kProxySession [WHWLProxySession sharedInstance]

typedef void (^WLProxyRequestProcessBlock)(NSString *result);
typedef void (^WLProxyRequestSuccessBlock)(NSString *result);
typedef void (^WLProxyRequestFailureBlock)(NSString *result);

//==============================================================================

// 上传/下载回调函数
static int OnProcessCB(const char* session,
					   int         command,
					   const char* result,
					   const char* custom,
					   LPARAM      lParam);

//==============================================================================

// 声明静态变量
static NSString *_config = nil;

@interface WHWLProxySession ()

@property (nonatomic, assign) NSStringEncoding encoding;

@property (nonatomic, strong) NSMutableDictionary *processDict;

@property (nonatomic, strong) NSMutableDictionary *successDict;

@property (nonatomic, strong) NSMutableDictionary *failureDict;

@property (nonatomic, strong) NSMutableDictionary *methodDict;

@end

@implementation WHWLProxySession

+ (instancetype)sharedInstance
{
	static dispatch_once_t once;
	static WHWLProxySession *_instance;
	dispatch_once(&once, ^{
		_instance = [[self alloc] init];
		_instance.encoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
		_instance.processDict = [NSMutableDictionary dictionary];
		_instance.successDict = [NSMutableDictionary dictionary];
		_instance.failureDict = [NSMutableDictionary dictionary];
		_instance.methodDict = [NSMutableDictionary dictionary];
	});
	return _instance;
}

+ (NSString *)requestWithSession:(NSString *)session
                          Method:(WLProxyTaskMethod)method
                          config:(NSString *)config
                          params:(NSString *)params
                          custom:(NSString *)custom
                        progress:(void (^)(NSString *result))progress
                         success:(void (^)(NSString *result))success
                         failure:(void (^)(NSString *result))failure
{
    // 校验代理地址能否连通 -- add by wangpeng@20230328
    BOOL ret = [self checkSocketConnectedWithProxy:config];
    if (!ret)
    {
        if (failure)
        {
            NSString *msg = [self getErrorMessageWithErrorCode:WHE_COMM_CONNECT];
            dispatch_async(dispatch_get_main_queue(), ^
            {
                if (failure)
                {
                    failure(msg);
                }
            });
        }
        return nil;
    }
    
	return [kProxySession requestWithSession:(NSString *)session
                                      Method:method
                                      config:config
                                      params:params
                                      custom:custom
                                    progress:progress
                                     success:success
                                     failure:failure];
}

- (NSString *)requestWithSession:(NSString *)session
                          Method:(WLProxyTaskMethod)method
                          config:(NSString *)config
                          params:(NSString *)params
                          custom:(NSString *)custom
                        progress:(WLProxyRequestProcessBlock)progress
                         success:(WLProxyRequestSuccessBlock)success
                         failure:(WLProxyRequestFailureBlock)failure
{
    wlproxy::CInvoker *invoker = nil;
    WHProxyInvoker *tmp = [[WHProxyManage sharedInstance] getInvokeWithConfig:session];
    if(tmp.invoker){
        invoker = tmp.invoker;
    }else{
        // 任务参数
        const char *param = [session cStringUsingEncoding:self.encoding];
        
        // 初始化任务
        invoker = new wlproxy::CInvoker();
        int invokerRet = invoker->Initialize(param);
        if(invokerRet != 0){
            NSString *msg = [WHWLProxySession getErrorTitleWithRe:invokerRet];
            NSLog(@"WLProxy---初始化返回提示：%@",msg);
            if (invokerRet != 0)
            {
                NSLog(@"WLProxy---请求返回提示：%@",[WHWLProxySession getErrorTitleWithRe:invokerRet]);
                NSString *msg = [WHWLProxySession getErrorMessageWithErrorCode:invokerRet];
                dispatch_async(dispatch_get_main_queue(), ^
                {
                    if (failure)
                    {
                        failure(msg);
                    }
                });
            }
            return @"";
        }
        
        tmp = [WHProxyInvoker new];
        tmp.invoker = invoker;
        [[WHProxyManage sharedInstance] setInvokeWithConfig:session invoke:tmp];
    }
    
	NSString *identifier = nil;
	
	// 请求参数
	const char *conf = [config cStringUsingEncoding:self.encoding];
	const char *cust = [custom cStringUsingEncoding:self.encoding];
	
	// 创建请求
	wlproxy::CRequest *request = NULL;
	int ret = invoker->CreateRequest(conf, cust, request);
	if (ret != 0 || request == NULL)
	{
        NSLog(@"WLProxy---请求返回提示：%@",[WHWLProxySession getErrorTitleWithRe:ret]);
        NSString *msg = [WHWLProxySession getErrorMessageWithErrorCode:ret];
        dispatch_async(dispatch_get_main_queue(), ^
        {
            if (failure)
            {
                failure(msg);
            }
        });
		return identifier;
	}
	
	// 获取唯一标识
	char *sess = NULL;
	request->GetSession(sess);
	if (ret != 0 || sess == NULL)
	{
        NSLog(@"WLProxy---请求返回提示：%@",[WHWLProxySession getErrorTitleWithRe:ret]);
        NSString *msg = [WHWLProxySession getErrorMessageWithErrorCode:ret];
        dispatch_async(dispatch_get_main_queue(), ^
        {
            if (failure)
            {
                failure(msg);
            }
        });
		return identifier;
	}
	identifier = [NSString stringWithCString:sess encoding:self.encoding];
        
     
    if(!tmp.requestIdArr){
        tmp.requestIdArr = [NSMutableArray new];
    }
    [tmp.requestIdArr addObject:identifier];
    [[WHProxyManage sharedInstance] setInvokeWithConfig:session invoke:tmp];
	
	// 把闭包存进字典
	if (progress)
	{
		[self.processDict setObject:progress forKey:identifier];
	}
	if (success)
	{
		[self.successDict setObject:success forKey:identifier];
	}
	if (failure)
	{
		[self.failureDict setObject:failure forKey:identifier];
	}
	
	[self.methodDict setObject:@(method) forKey:identifier];
	
	// 开始请求
	LPARAM lpCB = (LPARAM)(__bridge void *)self;
    NSLog(@"WLProxy---请求开始begin");
	
	switch (method)
	{
        case WLProxyTaskMethodAsyncGet:
        {
            dispatch_async(dispatch_get_global_queue(0, 0), ^
            {
                const char *pams = [params cStringUsingEncoding:self.encoding];
                int ret = request->StartGet(pams, OnProcessCB, lpCB);
                NSString *msg = [WHWLProxySession getErrorTitleWithRe:ret];
                NSLog(@"WLProxy---请求返回提示：%@",msg);
                if (ret != 0)
                {
                    NSString *msg = [WHWLProxySession getErrorMessageWithErrorCode:ret];
                    [self checkTaskValidWithID:identifier ret:ret errorMsg:msg];
                }
            });
        }
        break;
        case WLProxyTaskMethodAsyncPut:
        {
            dispatch_async(dispatch_get_global_queue(0, 0), ^
            {
                const char *pams = [params cStringUsingEncoding:self.encoding];
                int ret = request->StartPut(pams, OnProcessCB, lpCB);
                NSString *msg = [WHWLProxySession getErrorTitleWithRe:ret];
                NSLog(@"WLProxy---请求返回提示：%@",msg);
                if (ret != 0)
                {
                    NSString *msg = [WHWLProxySession getErrorMessageWithErrorCode:ret];
                    [self checkTaskValidWithID:identifier ret:ret errorMsg:msg];
                }
            });
        }
        break;
		case WLProxyTaskMethodSyncGet:
		{
			char *result = NULL;
            const char *pams = [params cStringUsingEncoding:self.encoding];
			ret = request->Get(pams, OnProcessCB, lpCB, result);
            NSString *msg = [WHWLProxySession getErrorTitleWithRe:ret];
            NSLog(@"WLProxy---请求返回提示：%@",msg);
			[self syncFinishTaskWithID:identifier];
			if (ret != 0)
			{
				NSString *datas = [NSString stringWithCString:result encoding:self.encoding];
				[self checkTaskValidWithID:identifier ret:ret errorMsg:datas];
			}
		}
			break;
			
		case WLProxyTaskMethodSyncPut:
		{
			char *result = NULL;
            const char *pams = [params cStringUsingEncoding:self.encoding];
			ret = request->Put(pams, OnProcessCB, lpCB, result);
            NSString *msg = [WHWLProxySession getErrorTitleWithRe:ret];
            NSLog(@"WLProxy---请求返回提示：%@",msg);
			[self syncFinishTaskWithID:identifier];
			if (ret != 0)
			{
				NSString *datas = [NSString stringWithCString:result encoding:self.encoding];
				[self checkTaskValidWithID:identifier ret:ret errorMsg:datas];
			}
		}
			break;
			
		default:
			break;
	}
	
	return identifier;
}

// 错误的话直接block返回
- (void)checkTaskValidWithID:(NSString *)identifier
						 ret:(NSInteger)ret
					errorMsg:(NSString*)msg
{
    if (ret != 0 && ret != 26)
    {
        dispatch_async(dispatch_get_main_queue(), ^
        {
            WLProxyRequestFailureBlock failureBlock = [self.failureDict objectForKey:identifier];
            if (failureBlock)
            {
                failureBlock(msg);
            }
        });
    }
}

//---------------------------------------------------------

// 解析代理地址，检测代理服务器能否链连通
+ (BOOL)checkSocketConnectedWithProxy:(NSString *)proxy
{
    // 解析出代理地址
    __block NSString *addr = nil;
    __block int       port = 0;
    BOOL ret = [self parseProxy:proxy block:^(NSString *address, int pot) {
        addr = address;
        port = pot;
    }];
    
    if (!ret)
    {
        return NO;
    }
    
    // 校验代理地址能否连通
    return [self checkSocketConnectedWithAddress:addr port:port];
}

// 解析代理
+ (BOOL)parseProxy:(NSString *)data block:(void (^)(NSString *address, int port))block
{
    XmlHelper *xmlHelper = [[XmlHelper alloc] init];
    if (![xmlHelper parse:data] || ![xmlHelper seek:@"param"])
    {
        return NO;
    }
    
    NSString *proxy = [xmlHelper childText:@"proxy"];
    NSArray *arr = [proxy componentsSeparatedByString:@":"];
    if (arr.count < 2)
    {
        return NO;
    }
    
    NSString *address = arr[0];
    int port = [arr[1] intValue];
    
    if (block)
    {
        block(address, port);
    }
    
    return YES;
}

// 检测代理服务器能否链连通
+ (BOOL)checkSocketConnectedWithAddress:(NSString *)address port:(int)port
{
    // 1、创建Socket
    // 第一个参数是 domain 协议蔟 指定IPv4
    // 第二个参数是 type   socket的类型  流socket -- 数据报socket
    // 第三个参数是 protocol 协议
    
    // 返回值 如果创建成功，返回的事socket的描述符，失败则为-1
    int clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    
    // 2、连接服务器
    // 第一个参数 套接字描述符
    // 第二个参数 只想数据结构sockaddr的指针，其中包括 目前端口和IP地址
    // 第三个参数 参数二sockaddr的长度，可以通过sizeof(struct sockaddr)获得
    
    const char *ip = [address cStringUsingEncoding:NSUTF8StringEncoding];
    struct sockaddr_in addr;
    addr.sin_family = AF_INET; // IPv4
    addr.sin_addr.s_addr = inet_addr(ip);
    addr.sin_port = htons(port);
    
    // 返回值 成功返回0，失败返回非0，错误码GetLastError()
    int result = connect(clientSocket, (const struct sockaddr *)&addr, sizeof(addr));
    
    // 3、关闭连接
    close(clientSocket);
    
    return result == 0;
}

// 拼接错误码xml
+ (NSString *)getErrorMessageWithErrorCode:(NSInteger)errorCode
{
    XmlHelper *xmlHelper = [XmlHelper xmlHelperWithEncoding:NSUTF8StringEncoding];
    [xmlHelper addChild:YES name:@"info"];

    [xmlHelper addChild:YES name:@"error_code" value:errorCode];
    [xmlHelper parent];
    [xmlHelper addChild:YES name:@"error_msg" text:@"请求失败"];

    [xmlHelper root];
    return [xmlHelper xmlString:NO];
}

//---------------------------------------------------------

#pragma mark - 任务结束

// 同步任务结束
- (void)syncFinishTaskWithID:(NSString *)identifier
{
    WHProxyInvoker *tmp = [[WHProxyManage sharedInstance] getInvokeWithRequestId:identifier];
    if(tmp.invoker){
        const char *sess = [identifier cStringUsingEncoding:self.encoding];
        wlproxy::CRequest *request = tmp.invoker->FindRequest(sess);
        
        if (request)
        {
            tmp.invoker->DestroyRequest(request);
        }
        
        [self removeBlockCacheIdentifier:identifier isCancel:NO];
    }
}

// 异步任务需要在任务开启线程释放
- (void)asyncFinishTaskWithID:(NSString *)identifier
{
    WHProxyInvoker *tmp = [[WHProxyManage sharedInstance] getInvokeWithRequestId:identifier];
    if(tmp.invoker){
        const char *sess = [identifier cStringUsingEncoding:self.encoding];
        wlproxy::CRequest *request = tmp.invoker->FindRequest(sess);
        
        if (request)
        {
            WLProxyTaskMethod method = (WLProxyTaskMethod)[[self.methodDict objectForKey:identifier] intValue];
            
            if (method == WLProxyTaskMethodAsyncGet)
            {
                request->StopGet();
            }
            else if (method == WLProxyTaskMethodAsyncPut)
            {
                request->StopPut();
            }
            tmp.invoker->DestroyRequest(request);
            NSLog(@"WLProxy---请求结束后 ： DestroyRequest");
        }
        
        [self removeBlockCacheIdentifier:identifier isCancel:NO];
    }
}

// 移除block缓存
- (void)removeBlockCacheIdentifier:(NSString *)identifier isCancel:(bool) isCancel
{
	[self.processDict removeObjectForKey:identifier];
	[self.successDict removeObjectForKey:identifier];
	[self.failureDict removeObjectForKey:identifier];
	[self.methodDict removeObjectForKey:identifier];
    WHProxyInvoker *tmp = [[WHProxyManage sharedInstance] getInvokeWithRequestId:identifier];
    if(tmp){
        for (NSString *a in tmp.requestIdArr) {
            if([a isEqualToString:identifier]){
                [tmp.requestIdArr removeObject:a];
                break;
            }
        }
    }
    if(!isCancel){
        [[WHProxyManage sharedInstance] removeNullInvoke];
    }
}

#pragma mark - 任务管理

// 取消请求
+ (void)cancelRequestWithIdentifier:(NSString *)identifier
{
	if (!identifier)
	{
		return;
	}
    WHProxyInvoker *tmp = [[WHProxyManage sharedInstance] getInvokeWithRequestId:identifier];
    if(tmp.invoker){
        const char *sess = [identifier cStringUsingEncoding:kProxySession.encoding];
        wlproxy::CRequest *request = tmp.invoker->FindRequest(sess);
        
        if (request)
        {
            request->Abort();
            tmp.invoker->DestroyRequest(request);
        }
        
        [kProxySession removeBlockCacheIdentifier:identifier isCancel:YES];
    }
}

// 暂停请求
+ (void)pauseRequestWithIdentifier:(NSString *)identifier
{
	if (!identifier)
	{
		return;
	}
    WHProxyInvoker *tmp = [[WHProxyManage sharedInstance] getInvokeWithRequestId:identifier];
    if(tmp.invoker){
        // 放到子线程，不然底层会出现任务等待，导致卡住主线程
        dispatch_async(dispatch_get_global_queue(0, 0), ^
        {
            const char *sess = [identifier cStringUsingEncoding:kProxySession.encoding];
            wlproxy::CRequest *request = tmp.invoker->FindRequest(sess);
            
            if (request)
            {
                request->Pause(true);
            }
        });
    }
}

// 继续请求
+ (void)resumeRequestWithIdentifier:(NSString *)identifier
{
	if (!identifier)
	{
		return;
	}
    WHProxyInvoker *tmp = [[WHProxyManage sharedInstance] getInvokeWithRequestId:identifier];
    if(tmp.invoker){
        // 放到子线程，不然底层会出现任务等待，导致卡住主线程
        dispatch_async(dispatch_get_global_queue(0, 0), ^
        {
            const char *sess = [identifier cStringUsingEncoding:kProxySession.encoding];
            wlproxy::CRequest *request = tmp.invoker->FindRequest(sess);
            
            if (request)
            {
                request->Pause(false);
            }
        });
    }
}

//==============================================================================

static int OnProcessCB(const char* session,
					   int         command,
					   const char* result,
					   const char* custom,
					   LPARAM      lParam)
{
	WHWLProxySession *task = (__bridge WHWLProxySession *)(void *)lParam;
	NSString *datas = [NSString stringWithCString:result encoding:task.encoding];

	NSString *identifier = [NSString stringWithCString:session encoding:task.encoding];
	NSLog(@"WLProxy---identifier = %@", identifier);
	
	WLProxyTaskMethod method = (WLProxyTaskMethod)[[task.methodDict objectForKey:identifier] intValue];
	WLProxyRequestProcessBlock processBlock = [task.processDict objectForKey:identifier];
	WLProxyRequestSuccessBlock successBlock = [task.successDict objectForKey:identifier];
	WLProxyRequestFailureBlock failureBlock = [task.failureDict objectForKey:identifier];
	
	switch (command)
	{
		case WLP_INVOKER_CMD_BEGIN:
		{
			NSLog(@"WLProxy---begin datas = %@", datas);
            dispatch_async(dispatch_get_main_queue(), ^
            {
                if (datas)
                {
                    if (processBlock)
                    {
                        processBlock(datas);
                    }
                }
                else
                {
                    if (method == WLProxyTaskMethodAsyncGet ||
                        method == WLProxyTaskMethodAsyncPut)
                    {
                        [task asyncFinishTaskWithID:identifier];
                    }
                    if (failureBlock)
                    {
                        failureBlock(datas);
                    }
                }
            });
		}
			break;

		case WLP_INVOKER_CMD_END:
		{
			dispatch_async(dispatch_get_main_queue(), ^
			{
				if (datas)
				{
					if (successBlock)
					{
						successBlock(datas);
					}
				}
				else
				{
					if (failureBlock)
					{
						failureBlock(datas);
					}
				}
                if (method == WLProxyTaskMethodAsyncGet ||
                    method == WLProxyTaskMethodAsyncPut)
                {
                    [task asyncFinishTaskWithID:identifier];
                }
			});
		}
			break;

		case WLP_INVOKER_CMD_FAILED:
		{
			dispatch_async(dispatch_get_main_queue(), ^
			{
				if (method == WLProxyTaskMethodAsyncGet ||
					method == WLProxyTaskMethodAsyncPut)
				{
					[task asyncFinishTaskWithID:identifier];
				}
				if (failureBlock)
				{
					failureBlock(datas);
				}
			});
		}
			break;

		case WLP_INVOKER_CMD_STEP:
		{
			dispatch_async(dispatch_get_main_queue(), ^
			{
				if (datas)
				{
					if (processBlock)
					{
						processBlock(datas);
					}
				}
				else
				{
					if (method == WLProxyTaskMethodAsyncGet ||
						method == WLProxyTaskMethodAsyncPut)
					{
						[task asyncFinishTaskWithID:identifier];
					}
					if (failureBlock)
					{
						failureBlock(datas);
					}
				}
			});
		}
			break;

		default:
			break;
	}
	
	return 0;
}

+(void)terminateInvoker
{
    [[WHProxyManage sharedInstance] removeALLInvoke];
}

+(NSString *)getErrorTitleWithRe:(int) re{
    if(re == WHE_COMM_FAILED){
        return @"请求失败";
    }else if(re == WHE_COMM_SUCCESS){
        return @"成功";
    }else if(re == WHE_COMM_PARAM){
        return @"参数错误";
    }else if(re == WHE_COMM_DATA){
        return @"数据错误(入参内存/长度)";
    }else if(re == WHE_COMM_BUFFER){
        return @"内存错误(出参内存/长度)";
    }else if(re == WHE_COMM_MEMORY){
        return @"内存分配错误";
    }else if(re == WHE_COMM_NOT_INIT){
        return @"尚未初始化";
    }else if(re == WHE_COMM_CONFIG){
        return @"配置文件错误";
    }else if(re == WHE_COMM_LIBRARY){
        return @"动态库加载错误";
    }else if(re == WHE_COMM_NOT_REG){
        return @"未注册";
    }else if(re == WHE_COMM_REBOOT){
        return @"需重启系统";
    }else if(re == WHE_COMM_RESTART){
        return @"需重启服务";
    }else if(re == WHE_COMM_CONNECT){
        return @"连接错误";
    }else if(re == WHE_COMM_TIMEOUT){
        return @"超时错误";
    }else if(re == WHE_COMM_SEND){
        return @"发送错误";
    }else if(re == WHE_COMM_NETWORK){
        return @"网络错误";
    }else if(re == WHE_COMM_REQDATA){
        return @"错误请求数据";
    }else if(re == WHE_COMM_NOT_IMPL){
        return @"接口未实现";
    }else if(re == WHE_COMM_NOT_FOUND){
        return @"找不到相关资源或接收用户";
    }else if(re == WHE_COMM_UNAVILABLE){
        return @"服务未提供或未启动";
    }else if(re == WHE_COMM_DATA_TYPE){
        return @"数据类型错误";
    }else if(re == WHE_COMM_RES_EXIST){
        return @"资源已存在（用户URI或设备资源等）";
    }else if(re == WHE_COMM_RES_NOT_EXIST){
        return @"资源不存在（用户URI或设备资源等）";
    }else if(re == WHE_COMM_FROM){
        return @"缺少发送者标识";
    }else if(re == WHE_COMM_TO){
        return @"缺少接收者标识";
    }else if(re == WHE_COMM_ROUTE_NOT_EXIST){
        return @"路由不存在";
    }else if(re == WHE_COMM_DECODE){
        return @"解码错误";
    }else if(re == WHE_COMM_ENCODE){
        return @"编码错误";
    }else if(re == WHE_COMM_NO_AUTH){
        return @"未注册/未授权";
    }else if(re == WHE_COMM_REG_REQUIRED){
        return @"执行此命令需要注册";
    }else if(re == WHE_COMM_EXCESS_CONN){
        return @"已达到最大并发数";
    }else if(re == WHE_COMM_DISABLED){
        return @"资源不可用(可用次数为零)";
    }else if(re == WHE_COMM_LOCKED){
        return @"资源已锁定";
    }else if(re == WHE_COMM_PASSWD){
        return @"密码错误";
    }else if(re == WHE_COMM_PASSWD_LEN){
        return @"密码长度错误";
    }else if(re == WHE_COMM_CERT){
        return @"证书验证失败";
    }else if(re == WHE_COMM_UPLOAD){
        return @"正在上传文件";
    }else if(re == WHE_COMM_UPLOAD_VER){
        return @"版本号太低";
    }else if(re == WHE_COMM_UPLOAD_COUNT){
        return @"上传文件数目错误";
    }else if(re == WHE_COMM_UPLOAD_SIZE){
        return @"正在上传尺寸错误";
    }else if(re == WHE_COMM_PATH){
        return @"创建路径错误";
    }else if(re == WHE_COMM_FILE){
        return @"打开/创建/读写文件错误";
    }else if(re == WHE_COMM_NO_RECORD){
        return @"无相关记录";
    }else if(re == WHE_COMM_NO_MATCH){
        return @"不匹配/不符合";
    }else if(re == WHE_COMM_IGNORED){
        return @"已忽略";
    }else if(re == WHE_COMM_SERVER_ERROR){
        return @"内部服务错误";
    }else if(re == WHE_COMM_LOGIN_REPEAT){
        return @"重复登录（或异地登录）";
    }else if(re == WHE_COMM_SUBS_REFUSE){
        return @"拒绝被订阅";
    }else if(re == WHE_COMM_BUSY){
        return @"服务忙";
    }else if(re == WHE_COMM_WAITING){
        return @"等待中";
    }else if(re == WHE_COMM_COMPLETED){
        return @"已完成";
    }else{
        return @"请求失败";
    }
}

@end
